package com.yihaodian.search.controller;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.PriorityQueue;

import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TopDocs;

import com.google.common.collect.Maps;
import com.yihaodian.api.search.SearchResponse;
import com.yihaodian.api.search.misc.SearchHit;
import com.yihaodian.index.meta.shard.Shard;
import com.yihaodian.search.fetch.FetchSearchResult;
import com.yihaodian.search.query.QuerySearchResult;

public class QueryController {
	public ShardDoc[] sortDocs(Collection<QuerySearchResult> results) {
		/**
		 * TODO:
		 * Optimise the sort method.
		 */
		PriorityQueue<ShardDoc> queue = new PriorityQueue<ShardDoc>();
		for (QuerySearchResult result : results) {
			Shard shard = result.getShard();
			TopDocs topDocs = result.getTopDocs();
			for (ScoreDoc scoreDoc : topDocs.scoreDocs) {
				ShardDoc doc = new ShardDoc();
				doc.setDocId(scoreDoc.doc);
				doc.setScore(scoreDoc.score);
				doc.setShard(shard);
				queue.add(doc);
			}
		}
		
		ShardDoc[] sortDocs = new ShardDoc[queue.size()];
		ShardDoc doc;
		while ((doc=queue.poll()) != null) {
			queue.add(doc);
		}
		return sortDocs;
	}
	
	public Map<Shard, int[]> docIdsPerShard(ShardDoc[] shardDocs) {
        Map<Shard, List<Integer>> listResult = Maps.newHashMap();
        for (ShardDoc shardDoc : shardDocs) {
            List<Integer> list = listResult.get(shardDoc.getShard());
            if (list == null) {
                list = new ArrayList<Integer>();
                listResult.put(shardDoc.getShard(), list);
            }
            list.add(shardDoc.getDocId());
        }
        
        /**
         * TODO: Use a data structrue to store the docId and convert to int[];
         */
        Map<Shard, int[]> intResult = Maps.newHashMap();
        for (Map.Entry<Shard, List<Integer>> entry: listResult.entrySet()) {
        	List<Integer> value = entry.getValue();
        	int[] docIds = new int[value.size()];
        	for (int i = 0; i < value.size(); i++) {
        		docIds[i] = value.get(i);
        	}
        	intResult.put(entry.getKey(), docIds);
        }
        return intResult;
    }
	
	public SearchResponse merge(ShardDoc[] sortedDocs, Map<Shard, QuerySearchResult> queryResults, 
			Map<Shard, FetchSearchResult> fetchResults) {
		/**
		 * TODO:
		 * 1. More efficient way to get fetch result.(Streaming)
		 * 2. Merge facet.
		 */
		List<SearchHit> hits = new ArrayList<SearchHit>();
		if (!fetchResults.isEmpty()) {
			for (ShardDoc shardDoc : sortedDocs) {
				FetchSearchResult fetchResult = fetchResults.get(shardDoc.getShard());
				SearchHit hit = fetchResult.getHits()[fetchResult.next()];
				hits.add(hit);
			}
		}
		
		SearchResponse response = new SearchResponse();
		response.setHits(hits.toArray(new SearchHit[0]));
		return response;
	}
}
